/*
 * StrUtil.java
 *
 * Created on 2004��10��9��, ����1:04
 */

package jm.lib.util;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import java.util.List;
import java.util.Random;

/**
 *
 * @author Jiming Liu
 */
public final class StrUtil {
    
    private StrUtil(){
    }
    
    private static int assessBufLen(int len){
        return Math.max(512, len + len / 8);
    }
    
    
    public static final String UPPERCASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public static final String LOWERCASE_CHARS = "abcdefghijklmnopqrstuvwxyz";
    public static final String NUMBER_CHARS = "0123456789";
    public static final String OTHER_CHARS = " !#$%&'()*+,-./:;<=>?@[]^_`{|}~\\\"";
    static public final String PASSWORD_CHARS =
            UPPERCASE_CHARS + LOWERCASE_CHARS + NUMBER_CHARS
            + OTHER_CHARS.replaceAll("[ \\\\\"]", "");
    
    
    public static final String[] EMPTY_STRING_ARRAY = new String[0];
    
    /**
	 * 
	 * @param values
	 * @return
	 */
	public static String append(String ... values) {
		StringBuilder sb = new StringBuilder();
		append(sb, values);
		return sb.toString();
	}

	public static void append(StringBuilder sb, String ... values) {
//		if (null == values)
//			return;
//		int len = values.length;
//		if (0 == len)
//			return;
//		for (int i = 0; i < len; i++) {
//			sb.append(values[i]);
//		}
		for(String s: values) {
			sb.append(s);
		}
	}
	
	public static String join(char delimiter, int ... values) {
        if(null == values) return "";
        int len = values.length;
        if(0 == len) return "";
        if(1 == len){
            return String.valueOf(values[0]);
        }
        StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for(int i = 1; i < len; i++){
            sb.append(delimiter).append(values[i]);
        }
        return sb.toString();
	}
   
	public static String join(char delimiter, long ... values) {
        if(null == values) return "";
        int len = values.length;
        if(0 == len) return "";
        if(1 == len){
            return String.valueOf(values[0]);
        }
        StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for(int i = 1; i < len; i++){
            sb.append(delimiter).append(values[i]);
        }
        return sb.toString();
	}
   
    /**
	 * Join an array of Strings together.
	 * 
	 * @param delimiter
	 *            Token to place between Strings.
	 * @param pieces
	 *            Array of Strings to join.
	 * @return String presentation of joined Strings.
	 * 
	 */
    public static String join(char delimiter, String ... values){
        if(null == values) return "";
        int len = values.length;
        if(0 == len) return "";
        if(1 == len){
            return values[0];
        }
        StringBuilder sb = new StringBuilder(assessBufLen(24 * values.length));
        sb.append(values[0]);
        for(int i = 1; i < len; i++){
            sb.append(delimiter).append(values[i]);
        }
        return sb.toString();
    }
    
    /**
     * Join an array of Strings together.
     *
     * @param delimiter Token to place between Strings.
     * @param pieces Array of Strings to join.
     * @return String presentation of joined Strings.
     *
     */
    public static String join(String delimiter, String ... values){
        if(null == values) return "";
        int len = values.length;
        if(0 == len) return "";
        if(1 == len){
            return values[0];
        }
        StringBuilder sb = new StringBuilder(assessBufLen(24 * len));
        sb.append(values[0]);
        for(int i = 1; i < len; i++){
            sb.append(delimiter).append(values[i]);
        }
        return sb.toString();
    }
    
    /**
     * Join an Iteration of Strings together.
     *
     * <h5>Example</h5>
     *
     * <pre>
     *   // get Iterator of Strings ("abc","def","123");
     *   Iterator i = getIterator();
     *   out.print( TextUtils.join(", ",i) );
     *   // prints: "abc, def, 123"
     * </pre>
     *
     * @param delimiter Token to place between Strings.
     * @param pieces Iteration of Strings to join.
     * @return String presentation of joined Strings.
     */
    public static String join(String delimiter, Iterator pieces) {
        StringBuilder s = new StringBuilder(512);
        
        if(pieces.hasNext()) s.append(pieces.next().toString());
        while (pieces.hasNext()) {
            s.append(delimiter);
            s.append(pieces.next().toString());
        }
        
        return s.toString();
    }
    
    
    /**
     * Join a Collection of Strings together.
     *
     * @param delimiter Token to place between Strings.
     * @param pieces Collection of Strings to join.
     * @return String presentation of joined Strings.
     *
     * @see #join(String, java.util.Iterator)
     */
    public static String join(String delimiter, Collection pieces) {
        return join(delimiter, pieces.iterator());
    }
    
    /**
     * Return <code>string</code>, or <code>defaultString</code> if
     * <code>string</code> is <code>null</code> or <code>""</code>.
     * Never returns <code>null</code>.
     *
     * <p>Examples:</p>
     * <pre>
     * // prints "hello"
     * String s=null;
     * System.out.println(StrUtil.noNull(s,"hello");
     *
     * // prints "hello"
     * s="";
     * System.out.println(StrUtil.noNull(s,"hello");
     *
     * // prints "world"
     * s="world";
     * System.out.println(StrUtil.noNull(s, "hello");
     * </pre>
     *
     * @param str the String to check.
     * @param defaultString The default string to return if <code>string</code> is <code>null</code> or <code>""</code>
     * @return <code>string</code> if <code>string</code> is non-empty, and <code>defaultString</code> otherwise
     * @see #stringSet(java.lang.String)
     */
    public static String noNull(String str, String defaultString) {
        return null == str ? defaultString : str;
    }
    
    /**
     * Return <code>string</code>, or <code>""</code> if <code>string</code>
     * is <code>null</code>. Never returns <code>null</code>.
     * <p>Examples:</p>
     * <pre>
     * // prints 0
     * String s=null;
     * System.out.println(StrUtil.noNull(s).length());
     *
     * // prints 1
     * s="a";
     * System.out.println(StrUtil.noNull(s).length());
     * </pre>
     * @param string the String to check
     * @return a valid (non-null) string reference
     */
    public static String noNull(String string) {
        return noNull(string, "");
    }
    
    /**
     * Check whether <code>s</code> is empty
     * something equal <code>""</code> or <code>null</code>.
     * @param s the <code>String</code> to check
     * @return a boolean indicating whether the string was empty (or null)
     */
    public static boolean isEmpty(String s) {
        return (s == null) || s.length() == 0;
    }
    
    public static boolean isNotEmpty(String s) {
    	return !isEmpty(s);
    }
    
    /**
     * Test whether <code>s</code> is empty, white space will not count
     * @param s
     * @return
     */
    public static boolean isEmptyGreedy(String s) {
        return (s == null) || s.length() == 0 || s.trim().length() == 0;
    }
    
    /**
     * Test whether <code>s</code> is not empty, white space will not count
     * @param s
     * @return
     */
    public static boolean isNotEmptyGreedy(String s) {
        return !isEmptyGreedy(s);
    }
    
    /**
     *
     * @param s
     */
    public static void trim(String[] s){
        if(null == s) return;
        int len = s.length;
        for(int i = 0; i < len; i++){
            s[i] = s[i].trim();
        }
    }
    
    /*
     * 
     * 
     */
    public static String trimNonLetter(String s) {
    	if(null == s) return "";
    	int len = s.length();
    	if(0 == len) return "";
    	int st = 0;
    	int off = len - 1;
    	
    	for(; !Character.isLetter(s.charAt(st)); st++) {
    		;
    	}

    	for(; !Character.isLetter(s.charAt(off)); off--) {
    		;
    	}

    	return ((st > 0) || (off < len - 1)) ? s.substring(st, off+1) : s;
    	
    }
    
    /**
     * Remove all <code>'\r'</code> from <code>s</code>
     * @param s
     * @return
     */
    public static String trimCR(String s){
        return replace(s, "\r", null);
    }
    
    /**
     * Returns a string that has whitespace removed from
     * both ends of the String, as well as duplicate whitespace
     * removed from within the String.
     */
    public static String innerTrim(String s) {
        StringBuilder b = new StringBuilder(s);
        int index = 0;
        
        while ((b.length() != 0) && (b.charAt(0) == ' ')) {
            b.deleteCharAt(0);
        }
        
        while (index < b.length()) {
            if (Character.isWhitespace(b.charAt(index))) {
                if (((index + 1) < b.length()) && (Character.isWhitespace(b.charAt(index + 1)))) {
                    b.deleteCharAt(index + 1);
                    index--; // let's restart at this character!
                }
            }
            
            index++;
        }
        
        if (b.length() > 0) {
            int l = b.length() - 1;
            
            if (b.charAt(l) == ' ') {
                b.setLength(l);
            }
        }
        
        return b.toString();
    }
    
    /**
     * <p>Removes one newline from end of a String if it's there,
     * otherwise leave it alone.  A newline is &quot;<code>\n</code>&quot;,
     * &quot;<code>\r</code>&quot;, or &quot;<code>\r\n</code>&quot;.</p>
     *
     * <p>NOTE: This method changed in 2.0.
     * It now more closely matches Perl chomp.</p>
     *
     * <pre>
     * StringUtils.chomp(null)          = null
     * StringUtils.chomp("")            = ""
     * StringUtils.chomp("abc \r")      = "abc "
     * StringUtils.chomp("abc\n")       = "abc"
     * StringUtils.chomp("abc\r\n")     = "abc"
     * StringUtils.chomp("abc\r\n\r\n") = "abc\r\n"
     * StringUtils.chomp("abc\n\r")     = "abc\n"
     * StringUtils.chomp("abc\n\rabc")  = "abc\n\rabc"
     * StringUtils.chomp("\r")          = ""
     * StringUtils.chomp("\n")          = ""
     * StringUtils.chomp("\r\n")        = ""
     * </pre>
     *
     * @param str  the String to chomp a newline from, may be null
     * @return String without newline, <code>null</code> if null String input
     */
    public static String chomp(String str) {
        if (isEmpty(str)) {
            return str;
        }
        
        if (str.length() == 1) {
            char ch = str.charAt(0);
            if (ch == '\r' || ch == '\n') {
                return "";
            } else {
                return str;
            }
        }
        
        int lastIdx = str.length() - 1;
        char last = str.charAt(lastIdx);
        
        if (last == '\n') {
            if (str.charAt(lastIdx - 1) == '\r') {
                lastIdx--;
            }
            //} else if (last == '\r') {
            // why is this block empty?
            // just to skip incrementing the index?
        } else {
            lastIdx++;
        }
        return str.substring(0, lastIdx);
    }
    
    /**
     * <p>Removes <code>separator</code> from the end of
     * <code>str</code> if it's there, otherwise leave it alone.</p>
     *
     * <p>NOTE: This method changed in version 2.0.
     * It now more closely matches Perl chomp.
     * For the previous behavior, use {@link #substringBeforeLast(String, String)}.
     * This method uses {@link String#endsWith(String)}.</p>
     *
     * <pre>
     * StringUtils.chomp(null, *)         = null
     * StringUtils.chomp("", *)           = ""
     * StringUtils.chomp("foobar", "bar") = "foo"
     * StringUtils.chomp("foobar", "baz") = "foobar"
     * StringUtils.chomp("foo", "foo")    = ""
     * StringUtils.chomp("foo ", "foo")   = "foo "
     * StringUtils.chomp(" foo", "foo")   = " "
     * StringUtils.chomp("foo", "foooo")  = "foo"
     * StringUtils.chomp("foo", "")       = "foo"
     * StringUtils.chomp("foo", null)     = "foo"
     * </pre>
     *
     * @param str  the String to chomp from, may be null
     * @param separator  separator String, may be null
     * @return String without trailing separator, <code>null</code> if null String input
     */
    public static String chomp(String str, String separator) {
        if (isEmpty(str) || separator == null) {
            return str;
        }
        if (str.endsWith(separator)) {
            return str.substring(0, str.length() - separator.length());
        }
        return str;
    }
    
    
    
    /**
     * Escape chars that need slashes in front of them.
     * @param s the String to add escape characters to
     * @return the converted String
     */
    public static String slashes(String s) {
        s = noNull(s);
        
        int len = s.length();
        if(0 == len) return s;
        
        StringBuilder sb = new StringBuilder(assessBufLen(s.length()));
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (c == '\\' || c == '\"' || c == '\'') {
                sb.append('\\');
            }
            sb.append(c);
        }
        
        return sb.toString();
    }
    
    /**
     * Escape chars that need slashes in front of them.
     * @param s the String to add escape characters to
     * @return the converted String
     */
    public static String slashesForJS(String s) {
        s = noNull(s);
        
        int len = s.length();
        if(0 == len) return s;
        
        StringBuilder sb = new StringBuilder(assessBufLen(s.length()));
        
        for (int i = 0; i < len; i++) {
            char c = s.charAt(i);
            if (c == '\\' || c == '\"' || c == '\'') {
                sb.append("\\\\");
            }
            
            sb.append(c);
        }
        
        return sb.toString();
    }
    
    /**
     *
     * @param str
     * @param size
     * @param padChar
     * @return
     */
    public static String leftPad(String str, int size, char padChar) {
        if (null == str) return repeat(padChar, size);
        
        int pads = size - str.length();
        if (pads <= 0) {
            return str; // returns original String when possible
        }
        StringBuilder sb = new StringBuilder(size);
        repeat(padChar, pads, sb);
        sb.append(str);
        return sb.toString();
    }
    
    /**
     *
     * @param str
     * @param size
     * @param padChar
     * @return
     */
    public static String rightPad(String str, int size, char padChar) {
        if (null == str) return repeat(padChar, size);
        
        int pads = size - str.length();
        if (pads <= 0) {
            return str; // returns original String when possible
        }
        StringBuilder sb = new StringBuilder(size);
        sb.append(str);
        repeat(padChar, pads, sb);
        return sb.toString();
    }
    
    /**
     *
     * @param c
     * @param times
     * @return
     */
    public static String repeat(char c, int times){
        StringBuilder sb = new StringBuilder(times);
        repeat(c, times, sb);
        return sb.toString();
    }
    
    /**
     *
     * @param c
     * @param times
     * @param buf
     */
    public static void repeat(char c, int times, StringBuilder buf){
        for (int i = 0; i < times; i++) {
            buf.append(c);
        }
    }
    
    /**
     *
     * @param s
     * @param times
     * @return
     */
    public static String repeat(String s, int times){
        StringBuilder sb = new StringBuilder(s.length() * times);
        for ( int i = 0; i < times; i++ ) sb.append( s );
        return sb.toString();
    }
    
    /**
     *
     * @param s
     * @param times
     * @return
     */
    public static String repeat(String s, int times, String seperator){
        StringBuilder sb = new StringBuilder((s.length() + seperator.length()) * times);
        if(times>0) sb.append(s);
        for ( int i = 1; i < times; i++ ) {
            sb.append(seperator).append(s);
        }
        return sb.toString();
    }
    
    
    /**
     * Replace all occurances of a string within another string.
     *
     * @see #replace(String text, String repl, String with, int max)
     * @param text  text to search and replace in
     * @param repl  String to search for
     * @param with  String to replace with
     * @return the text with any replacements processed
     */
    public static String replace(String text, String repl, String with) {
        return replace(text, repl, with, -1);
    }
    
    /**
     * Replace a string with another string inside a larger string,
     * for the first <code>max</code> values of the search string.  A
     * <code>null</code> reference is passed to this method is a
     * no-op.
     * @param text text to search and replace in
     * @param repl String to search for
     * @param with String to replace with
     * @param max maximum number of values to replace, or
     * <code>-1</code> if no maximum
     * @return the text with any replacements processed
     */
    public static String replace(String text, String repl, String with, int max) {
        if (isEmpty(text)) return "";
        if(isEmpty(repl)) return text;
        if(null == with) with = "";
        // REFACTOR: if null == with , do not use "", just skip append
        
        int pos = 0, end = 0;
        pos = text.indexOf(repl, end);
        if(pos < 0) return text;
        int len = repl.length();
        StringBuilder buf = new StringBuilder(text.length() + len<with.length()?(text.length()>>3):0);
        do{
            buf.append(text.substring(end, pos)).append(with);
            end = pos + len;
            if (--max == 0) break;
        }while ((pos = text.indexOf(repl, end)) != -1);
        buf.append(text.substring(end));
        return buf.toString();
    }
    
    /**
     * 
     * @param str
     * @param seperator
     * @return
     */
    public static String[] split(String str, char seperator) {
    	String[] result = new String[2];
    	int index = str.indexOf(seperator);
    	if(-1 == index) {
    		result[0] = str;
    		result[1] = "";
    	} else {
    		result[0] = str.substring(0, index);
    		result[1] = str.substring(index+1);
    	}
    	return result;
    }
    
    /**
     * Split <code>str</code> with <code>','</code> and will trim each substring
     * @param str
     * @return
     */
    public static String[] split(String str) {
        return split(str, ",", -1, true);
    }
    
    /**
     * call <code>split(String str, String seperator, int limit, boolean trim)</code> with <code>trim = false</code>
     * and <code>limit=-1</code>
     * @param str
     * @param seperator
     *
     * @return
     */
    public static String[] split(String str, String seperator) {
        return split(str, seperator, -1, true);
    }
    
    /**
     *
     * @param str
     * @param seperator
     * @param trim whether trim the result strings
     * @return
     */
    public static String[] split(String str, String seperator, boolean trim) {
        return split(str, seperator, -1, trim);
    }
    
    /**
     *
     * @param str
     * @param seperator
     * @param limit
     * @return
     */
    public static String[] split(String str, String seperator, int limit) {
        return split(str, seperator, limit, true);
    }
    
    /**
     * Splits the provided text into a list, based on a given separator.
     * The separator is not included in the returned String array.
     * The maximum number of splits to perfom can be controlled.
     * A null separator will cause parsing to be on whitespace.
     *
     * <p>This is useful for quickly splitting a string directly into
     * an array of tokens, instead of an enumeration of tokens (as
     * <code>StringTokenizer</code> does).
     *
     * @param str The string to parse.
     * @param separator Characters used as the delimiters. If
     * <code>null</code>, splits on whitespace.
     * @param limit The maximum number of elements to include in the
     * @param trim whether trim the result strings
     * list.  A zero or negative value implies no limit.
     * @return an array of parsed Strings
     */
    public static String[] split(String str, String separator, int limit, boolean trim) {
        if (isEmpty(str)) {
            return EMPTY_STRING_ARRAY;
        }
        if (isEmpty(separator)) {
            return new String[] {str};
        }
        
        List<String> result = new ArrayList<String>();
        int pos = 0;
        int delPos = 0;
        while ((delPos = str.indexOf(separator, pos)) != -1) {
            result.add(str.substring(pos, delPos));
            pos = delPos + separator.length();
        }
        if (str.length() > 0 && pos <= str.length()) {
            // Add rest of String, but not in case of empty input.
            result.add(str.substring(pos));
        }
        String[] ss = new String[result.size()];
        result.toArray(ss);
        if(trim) StrUtil.trim(ss);
        return ss;
    }
    
    /**
     * Capitalize a <code>String</code>, changing the first letter to
     * upper case as per {@link Character#toLowerCase(char)}.
     * No other letters are changed.
     * @param str the String to capitalize, may be null
     * @return the capitalized String, <code>null</code> if null
     */
    public static String capitalize(String str) {
        return changeFirstCharacterCase(str, true);
    }
    
    /**
     * Uncapitalize a <code>String</code>, changing the first letter to
     * lower case as per {@link Character#toLowerCase(char)}.
     * No other letters are changed.
     * @param str the String to uncapitalize, may be null
     * @return the uncapitalized String, <code>null</code> if null
     */
    public static String uncapitalize(String str) {
        return changeFirstCharacterCase(str, false);
    }
    private static String changeFirstCharacterCase(String str, boolean capitalize) {
        if (isEmpty(str)) {
            return str;
        }
        boolean isCapitalized = Character.isUpperCase(str.charAt(0));
        if(capitalize){
            if(isCapitalized) return str;
            StringBuilder sb = new StringBuilder(str);
            sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
            return sb.toString();
        }else if(isCapitalized){
            StringBuilder sb = new StringBuilder(str);
            sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
            return sb.toString();
        }else{
            return str;
        }
        
    }
    
    /**
     * Get the String that is nested in between two instances of the
     * same String. If str is null, will return null
     *
     * @param str  the string containing nested-string
     * @param tag  the string before and after nested-string
     * @return the string that was nested, or null
     * @throws NullPointerException if tag is null
     */
    public static String getNestedString(String str, String tag) {
        return getNestedString(str, tag, tag);
    }
    
    /**
     * Get the string that is nested in between two strings.
     *
     * @param str  the string containing nested-string
     * @param open  the string before nested-string
     * @param close  the string after nested-string
     * @return the string that was nested, or null
     * @throws NullPointerException if open or close  is null
     */
    public static String getNestedString(String str, String open, String close) {
        if (str == null) {
            return "";
        }
        int start = str.indexOf(open);
        if (start != -1) {
            int end = str.indexOf(close, start + open.length());
            if (end != -1) {
                return str.substring(start + open.length(), end);
            }
        }
        return "";
    }
    
    /**
     *
     * @param str
     * @param open
     * @param close
     * @return
     */
    public static String[] getNestedStrings(String str, String open, String close) {
        // TODO:
        throw new RuntimeException("Not implemented!");
    }
    
    /**
     * How many times is the substring in the larger string.
     * Null returns 0.
     *
     * @param str  the string to check
     * @param sub  the substring to count
     * @return the number of occurances, 0 if the string is null
     * @throws NullPointerException if sub is null
     */
    public static int countMatches(String str, String sub) {
        if (isEmpty(str) || isEmpty(sub)) {
            return 0;
        }
        int count = 0;
        int idx = 0;
        while ((idx = str.indexOf(sub, idx)) != -1) {
            count++;
            idx += sub.length();
        }
        return count;
    }
    
    //TODO: need refactor
    /**
     *
     * @param s
     * @return
     */
    public static String decodeURL1(String s){
        int pos = s.indexOf('%');
        if(pos < 0) return s;
        int len = s.length() - 1;
        StringBuilder sb = new StringBuilder(len);
        sb.append(s, 0, pos);
        int i = pos;
        for(; i < len; i++){
            char a = s.charAt(i);
            if(a == '%'){
                char a1 = s.charAt(i+1);
                switch(a1){
                    case '1':
                        i++;
                        sb.append('&');
                        break;
                    case '2':
                        i++;
                        sb.append('=');
                        break;
                    case '%':
                        i++;
                        sb.append('%');
                        break;
                    default:
                        sb.append('%');
                }
            }else{
                sb.append(a);
            }
        }
        for(;i<len+1; i++){
            sb.append(s.charAt(i));
        }
        return sb.toString();
    }
    
    /**
     * replace '<b>&</b>' to '<b>%1</b>', '<b>=</b>' to '<b>%2</b>', '<b>%</b>' to '<b>%%</b>'
     * @param s
     * @return
     */
    public static String encodeURL1(String s){
        int len = s.length();
        int pos = -1;
        SearchChar:
            for(int i = 0; i < len; i++){
                switch(s.charAt(i)){
                    case '&':
                    case '=':
                    case '%':
                        pos = i;
                        break SearchChar;
                }
            }
            
            
            if(pos == -1) return s;
            StringBuilder sb = new StringBuilder(len + len>>3);
            sb.append(s.subSequence(0, pos));
            for(int i = pos; i < len; i++){
                char a = s.charAt(i);
                switch(a){
                    case '&':
                        sb.append(s.substring(pos, i));
                        pos = i+1;
                        sb.append("%1");
                        break;
                    case '=':
                        sb.append(s.substring(pos, i));
                        pos = i+1;
                        sb.append("%2");
                        break;
                    case '%':
                        sb.append(s.substring(pos, i));
                        pos = i+1;
                        sb.append("%%");
                        break;
                }
            }
            sb.append(s.substring(pos));
            return sb.toString();
    }
    
    
    // Misc
    //--------------------------------------------------------------------------
    
    /**
     * Checks if the String contains only certain chars.
     *
     * @param str the String to check
     * @param valid an array of valid chars
     * @return true if it only contains valid chars and is non-null
     */
    public static boolean containsOnly(String str, char ... valid) {
        if (str == null || valid == null) {
            return false;
        }
        
        int len = str.length();
        int validSize = valid.length;
        
        for (int i = 0; i < len; i++) {
            boolean contains = false;
            for (int j = 0; j < validSize; j++) {
                if (valid[j] == str.charAt(i)) {
                    contains = true;
                    break;
                }
            }
            if (!contains) {
                return false;
            }
        }
        
        return true;
    }
    
    
    /**
     *
     * @deprecated
     * @param str
     * @return
     */
    static public int hash(String str) {
		if (null == str)
			return 0;
		byte key[] = str.getBytes();
		int result = 0x238F13AF * key.length;

		for (int i = 0; i < key.length; i++) {
			result = (result + (key[i] << (i * 5 % 24))) & 0x7FFFFFFF;
		}
		result = (1103515243 * result + 12345) & 0x7FFFFFFF;
		return result;
	}
    
    static final char[] cArr = {
        '0','1','2','3','4'
                ,'5','6','7','8','9'
                ,'A','B','C','D','E','F','G'
                ,'H','I','J','K','L','M','N'
                ,'O','P','Q','R','S','T'
                ,'U','V','W','X','Y','Z'
                ,'a','b','c','d','e','f','g'
                ,'h','i','j','k','l','m','n'
                ,'o','p','q','r','s','t'
                ,'u','v','w','x','y','z'
                //, '_', '.'
    };
    public static final int MAX_RADIX = cArr.length;
    static final Random r = new Random();
    /**
     *
     * @return
     */
    public static char getRandomChar(){
        return cArr[r.nextInt(MAX_RADIX)];
    }
    
    /**
     *
     * @param sb
     * @param len
     * @param radix
     * @exception IllegalArgumentException <code>sb</code> is null
     *                                     <code>len</code> less than 1
     *                                     <code>radix</code> not in qualified range
     */
    public static void appendRandomChars(StringBuilder sb, int len, int radix){
        if(null == sb || len <= 0 || radix <=0 || radix > MAX_RADIX){
            throw new IllegalArgumentException(
                    MessageFormat.format("Argument({0}, {1}, {2}) error!", sb, len, radix)
                    );
        }
        byte[] bytes = new byte[len];
        r.nextBytes(bytes);
        for(int i = 0; i < len; i++){
            sb.append(cArr[(bytes[i]&0x7F)%radix]);
        }
    }
    
    /**
     * Returns a string representation of the first argument in the
     * radix specified by the second argument.
     * <p>
     * If the radix is smaller than <code>Character.MIN_RADIX</code>
     * or larger than <code>MAX_RADIX(62)</code>, then the radix
     * <code>MAX_RADIX</code> is used instead.
     * <p>
     * The remaining characters of the result represent the magnitude
     * of the first argument. If the magnitude is zero, it is
     * represented by a single zero character <code>'0'</code>
     * (<code>'&#92;u0030'</code>); otherwise, the first character of
     * the representation of the magnitude will not be the zero
     * character.  The following ASCII characters are used as digits:
     * <blockquote><pre>
     *   0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
     * </pre></blockquote>
     * These are <code>'&#92;u0030'</code> through
     * <code>'&#92;u0039'</code> and <code>'&#92;u0061'</code> through
     * <code>'&#92;u007a'</code>. If <code>radix</code> is
     * <var>N</var>, then the first <var>N</var> of these characters
     * are used as radix-<var>N</var> digits in the order shown. Thus,
     * the digits for hexadecimal (radix 16) are
     * <code>0123456789abcdef</code>. If uppercase letters are
     * desired, the {@link java.lang.String#toUpperCase()} method may
     * be called on the result:
     * <blockquote><pre>
     * Long.toString(n, 16).toUpperCase()
     * </pre></blockquote>
     *
     * @param   i       a <code>long</code>to be converted to a string.
     * @param   radix   the radix to use in the string representation.
     * @return  a string representation of the argument in the specified radix.
     *      <b>NOTE:</b> No negative sign will be returned.
     * @see     java.lang.Character#MIN_RADIX
     */
    public static String toString(long i, int radix) {
        if (radix < Character.MIN_RADIX || radix > MAX_RADIX)
            radix = MAX_RADIX;
        char[] buf = new char[65];
        int charPos = 64;
        boolean negative = (i < 0);
        
        if (!negative) {i = -i;}
        
        while (i <= -radix) {
            buf[charPos--] = cArr[(int)(-(i % radix))];
            i = i / radix;
        }
        buf[charPos] = cArr[(int)(-i)];
        
        return new String(buf, charPos, (65 - charPos));
    }
    
    /**
     * 
     * @see StrUtil.verifyCheckSum
     * @param values
     * @param key
     * @return
     */
    public static String getCheckSum(String key, String ... values){
        if(null == values || null == key) return "";
        int len = values.length;
        StringBuilder sb = new StringBuilder(Math.max(key.length() * (len + 1), 32));
        for(int i = 0; i < len; i++){
            sb.append(values[i]);
        }
        sb.append(key);
        byte[] bytes = CodeUtil.md5(sb.toString());
        sb = null;
        return CodeUtil.byte2String(bytes);
    }
    
    /**
     *
     * @see StrUtil.getCheckSum
     * @param values
     * @param key
     * @param checkSum
     * @return
     */
    public static boolean verifyCheckSum(String checkSum, String key, String ... values){
        if(null == values || null == key || null == checkSum) return false;
        return checkSum.equals(getCheckSum(key, values));
    }
    
    
    /**
     * Truncate the string to <code>len</code> in byte, one Chinese character
     * eqauls two byte
     * @param s
     * @param len
     * @return
     */
    public static final String cutString(String s, int len){
        if (len < 1 || isEmpty(s))
			return "";
        int sLen = s.length();
        if(len >= sLen * 2) return s;
        int i = 0;
        for(; i < sLen && len > 0; i++){
            len -= s.charAt(i) > 255? 2: 1;
        }
        return s.substring(0, i);
    }
    
    /**
     * get string from tail
     * @param s
     * @param length the length of the result string
     * @return
     */
    public static String tail(String s, int length) {
    	if(null == s) return "";
    	int len = s.length();
    	if(len < length || length < 1) return s;
    	
    	return s.substring(len - length);
    }
    
    /**
     * tail(s, sep, false);
     * @param s
     * @param sep
     * @return
     */
    public static String tail(String s, String sep) {
    	return tail(s, sep, false);
    }
    /**
     * get substring after <code>sep</code>
     * @param s
     * @param sep
     * @param included include <code>sep</code> in result
     * @return
     */
    public static String tail(String s, String sep, boolean included) {
    	if(null == s) return "";
    	int len = s.length();
    	if(len == 0 || isEmpty(sep)) return s;
    	
    	int idx = s.lastIndexOf(sep);
    	if(-1 == idx) return s;
    	return s.substring(idx + (included?0:sep.length()));
    }
    
    /**
     * get substring before <code>sep</code>
     * @param s
     * @param sep
     * @param included include <code>sep</code> in result
     * @return
     */
    public static String head(String s, String sep, boolean included) {
    	if(null == s) return "";
    	int len = s.length();
    	if(len == 0 || isEmpty(sep)) return s;
    	
    	int idx = s.indexOf(sep);
    	if(-1 == idx) return s;
    	return s.substring(0, idx + (included?sep.length():0));
   	
    }
    
    /**
     * 将页面上的汉字转换为unicode
     *
     * @param inStr 要转换的中文字符串
     *
     * @return unicodeStr 转换后的unicode字符串
     */
    public static String GBToUnicode(String inStr) {
        // OPTIMIZE:
        //将要转换的字符串拆成字符数组
        char[] myBuffer = inStr.toCharArray();

        //要返回的unicode字符串
        String unicodeStr = "";

        //循环取出每个字符的unicode
        for (int i = 0; i < inStr.length(); i ++) {

            //得到汉字内码
            short s = (short) myBuffer[i];
            int j = new Integer("" + s).intValue();

            String unicode = Integer.toHexString(j);

            //拼接unicode字符串
            if(unicode.indexOf("ffff") != -1) {
                unicode = unicode.substring( 0, unicode.indexOf("ffff") ) +
                    unicode.substring( unicode.indexOf("ffff") + 4 );
            }

            unicodeStr += "&#x" + unicode + ";";
        }

        //返回
        return unicodeStr;
    }

    /**
     * 将得到的汉字参数从Utf-8转换为unicode字符串
     *
     * @param inStr 要转换的汉字参数从Utf-8字符串
     *
     * @return unicodeStr 转换后的unicode字符串
     */
    public static String UtfToUnicode( String s ) throws Exception {

        //得到UTF-8编码的字符串
        s = new String(s.getBytes("ISO-8859-1"), "UTF-8");

        //要返回的字符串
        String temp = "";

        //得到数组
        char[] charArray = s.toCharArray();

        //拼接unicode字符串
        for(int i = 0; i < charArray.length; i ++) {
          temp += "&#x" + Integer.toHexString(charArray[i])+ ";";
        }

        return temp;
    }

    
	/**
	 * 
	 * @param str
	 * @return
	 */
	public static String getPropertyName(String str) {
		String[] items = StrUtil.split(str.toLowerCase(), "_");
		StringBuilder sb = new StringBuilder();
		for (String s : items) {
			sb.append(StrUtil.capitalize(s));
		}
		sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
		return sb.toString();
	}

    
    /**
     * @deprecated
     * Using <code>cutString</code> instead
     * To truncate the string <code>s</code> length <code>length</code>.
     * One chinese character's length will be considered as 2
     * @param s the String
     * @param length the left string length
     * @return the truncated String
     */    
    public static String truncate(String s, int length) {
        if (length < 1 || isEmpty(s))
			return "";
        int len = s.length();
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < len && length > 0; i++){
            char c = s.charAt(i);
            if(c < 128){
                sb.append(c);
                length--;
            }else{
                if(length>1){
                    sb.append(c);
                }
                length -= 2;
            }
        }
        return sb.toString();
    }
    
    public static void main(String[] args){
        try{
            System.out.println(new Integer('\r'));
            System.out.println(new Integer('\n'));
            System.out.println(StrUtil.join("\r\n", "a", "b", "c", "d"));
            System.out.println(StrUtil.join("\r\n", new String[]{"a", "b", "c", "d"}));
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }
}


